﻿namespace H2.Streams
{
    using H2;
    using H2.DataTypes;
    using H2.Streams.Sections;
    using System;
    using System.Reflection;
    using System.Windows.Forms;

    public class MapStream : BaseStream
    {
        private H2.Streams.Sections.Bitmaps _bitmaps;
        private H2.Streams.Sections.Header _header;
        private H2.Streams.Sections.Index _index;
        private H2.Streams.Sections.Sbsp _sbsp;
        private H2.Streams.Sections.StringIDs _stringIDs;
        private H2.Streams.Sections.Tags _tags;
        private UnicodeCollection _unicode;
        internal int FileTableOffset;
        internal int IndexOffset;
        internal int MetaStart;
        internal TagInfo Scnr;
        internal int SecondaryMagic;
        internal int TagInfoCount;
        internal int TagInfoIndexOffset;

        public MapStream(string filepath, BaseStream.StatusChangeHandler statusChangedHandler) : base(filepath, statusChangedHandler)
        {
            this.Reload();
        }

        public void RefreshCachedValues()
        {
            IndexOffset = _header.IndexOffset;
            MetaStart = _header.MetaStart;
            FileTableOffset = _header.FileTableOffset;

            TagInfoIndexOffset = _index.TagInfoIndexOffset;
            TagInfoCount = _index.TagCount;
            SecondaryMagic = _index.SecondaryMagic;

            foreach (TagInfo ti in Tags.TagInfoCache)
                ti.ChangeOffset(ti.Offset, SecondaryMagic);
        }

        public int CalculatePaddingSize(int DataSize, int PaddingChunkSize)
        {
            int result = 0;
            Math.DivRem(DataSize, PaddingChunkSize, out result);
            if (result == 0)
            {
                return 0;
            }
            return (PaddingChunkSize - result);
        }

        public void MoveToTag(int i, H2.Streams.Sections.Tags.SearchType st)
        {
            base.Position = this.Tags[i, st].Offset;
        }

        public void MoveToTag(string Class, string Path)
        {
            base.Position = this.Tags[Class, Path].Offset;
        }

        public T ReadDataBlock<T>(bool IsSBSP, bool RetainCurrentPosition)
        {
            if (IsSBSP)
            {
                return (T) H2.Streams.Sections.Tags.BlockFromStream(this, (int) base.Position, typeof(T), this.Index.PrimaryMagic, RetainCurrentPosition);
            }
            return (T) H2.Streams.Sections.Tags.BlockFromStream(this, (int) base.Position, typeof(T), this.SecondaryMagic, RetainCurrentPosition);
        }

        public T ReadDataBlockAt<T>(int Offset, bool IsSBSP, bool RetainCurrentPosition)
        {
            if (IsSBSP)
            {
                return (T) H2.Streams.Sections.Tags.BlockFromStream(this, Offset, typeof(T), this.Index.PrimaryMagic, RetainCurrentPosition);
            }
            return (T) H2.Streams.Sections.Tags.BlockFromStream(this, Offset, typeof(T), this.SecondaryMagic, RetainCurrentPosition);
        }

        public Dependancy ReadDependancy()
        {
            Dependancy dependancy = new Dependancy();
            if (base.br.PeekChar() == 0xfffd)
            {
                dependancy.Class = "llun".ToCharArray();
                base.Position += 4L;
            }
            else
            {
                dependancy.Class = base.ReadChars(4);
            }
            dependancy.Path = this.Tags.GetTagPath(base.br.ReadInt32(), H2.Streams.Sections.Tags.SearchType.ID);
            return dependancy;
        }

        public Dependancy ReadDependancy(bool RetainCurrentPosition)
        {
            long position = base.Position;
            Dependancy dependancy = new Dependancy();
            if (base.br.PeekChar() == 0xfffd)
            {
                dependancy.Class = "llun".ToCharArray();
                base.Position += 4L;
            }
            else
            {
                dependancy.Class = base.ReadChars(4);
            }
            dependancy.Path = this.Tags.GetTagPath(base.br.ReadInt32(), H2.Streams.Sections.Tags.SearchType.ID);
            if (RetainCurrentPosition)
            {
                base.Position = position;
            }
            return dependancy;
        }

        public Dependancy ReadDependancyAt(int Offset, bool RetainCurrentPosition)
        {
            long position = base.Position;
            Dependancy dependancy = new Dependancy();
            base.Position = Offset;
            if (base.br.PeekChar() == 0xfffd)
            {
                dependancy.Class = "llun".ToCharArray();
                base.Position += 4L;
            }
            else
            {
                dependancy.Class = base.ReadChars(4);
            }
            dependancy.Path = this.Tags.GetTagPath(base.br.ReadInt32(), H2.Streams.Sections.Tags.SearchType.ID);
            if (RetainCurrentPosition)
            {
                base.Position = position;
            }
            return dependancy;
        }

        public int ReadReflexive()
        {
            int num = base.br.ReadInt32();
            if (num > 0)
            {
                int num2 = base.br.ReadInt32() - this.SecondaryMagic;
                base.Position = num2;
                return num;
            }
            base.br.ReadInt32();
            return num;
        }

        public int ReadReflexive(int Magic)
        {
            int num = base.br.ReadInt32();
            if (num > 0)
            {
                int num2 = base.br.ReadInt32() - Magic;
                base.Position = num2;
                return num;
            }
            base.br.ReadInt32();
            return num;
        }

        public int ReadReflexiveAt(int Offset)
        {
            base.Position = Offset;
            int num = base.br.ReadInt32();
            if (num > 0)
            {
                int num2 = base.br.ReadInt32() - this.SecondaryMagic;
                base.Position = num2;
            }
            return num;
        }

        public int ReadReflexiveAt(int Offset, int Magic)
        {
            base.Position = Offset;
            int num = base.br.ReadInt32();
            if (num > 0)
            {
                int num2 = base.br.ReadInt32() - Magic;
                base.Position = num2;
                return num;
            }
            base.br.ReadInt32();
            return num;
        }

        public StringID ReadStringID()
        {
            StringID gid = this.StringIDs[base.ReadUInt16()];
            base.ReadInt16();
            return gid;
        }

        public StringID ReadStringID(bool RetainCurrentPosition)
        {
            long position = base.Position;
            StringID gid = this.StringIDs[base.ReadUInt16()];
            base.ReadInt16();
            base.Position = position;
            return gid;
        }

        public StringID ReadStringIDAt(int Offset, bool RetainCurrentPosition)
        {
            long position = base.Position;
            base.Position = Offset;
            StringID gid = this.StringIDs[base.ReadUInt16()];
            base.ReadInt16();
            if (RetainCurrentPosition)
            {
                base.Position = position;
            }
            return gid;
        }

        public void Reload()
        {
            base.Status = "Loading Header...";
            Application.DoEvents();
            this.Header = new H2.Streams.Sections.Header(this);
            base.Status = "Loading StringIDs...";
            Application.DoEvents();
            this.StringIDs = new H2.Streams.Sections.StringIDs(this);
            base.Status = "Loading Index...";
            Application.DoEvents();
            this.Index = new H2.Streams.Sections.Index(this);
            base.Status = "Loading File Table...";
            Application.DoEvents();
            this.Tags = new H2.Streams.Sections.Tags(this);
            base.Status = "Loading Sbsps And Lightmaps...";
            Application.DoEvents();
            this.Sbsp = new H2.Streams.Sections.Sbsp(this);
            this.Sbsp.CreateCache();
            for (int i = 0; i < this.Sbsp.SbspInfoCache.Length; i++)
            {
                this.Sbsp.SbspInfoCache[i].LightMap.CreatePaletteCache();
            }
            base.Status = "Loading Unicode Tables...";
            Application.DoEvents();
            this.Unicode = new UnicodeCollection(this);
            base.Status = "Loading Bitmaps...";
            Application.DoEvents();
            this.Bitmaps = new H2.Streams.Sections.Bitmaps(this);
            base.Status = "Ready.";
        }

        public string Resign()
        {
            base.Status = "Resigning...";
            base.PushBookmark();
            Benchmark.Begin();
            
            int result = 0;
            int sizeCheck;
            const int bufferSize = 16384;

            base.Position = 2048;
            do
            {
                byte[] buffer = base.ReadBytes(bufferSize);
                sizeCheck = buffer.Length;
                for (int x = 0; x < buffer.Length; x += 4)
                {
                    result ^= BitConverter.ToInt32(buffer, x);
                }
            }
            while (sizeCheck == bufferSize);
            this.Header.Checksum = result;
            base.PopBookmark();
            Benchmark.End();
            return Benchmark.Result;
        }

        public void Write(Dependancy Dependancy)
        {
            if (Dependancy.ClassString == "null")
            {
                base.Position += 4L;
            }
            else
            {
                base.Write(Dependancy.Class);
            }
            int index = this.Tags.IndexOf(Dependancy.ClassString, Dependancy.Path);
            if (index != -1)
            {
                base.bw.Write(this.Tags[index, H2.Streams.Sections.Tags.SearchType.Index].ID);
            }
            else
            {
                base.bw.Write(index);
            }
        }

        public void Write(StringID StringID)
        {
            if (StringID.NOTINSTRINGIDTABLE)
            {
                base.bw.Write(0);
            }
            else
            {
                base.bw.Write(this.StringIDs.IndexOf(StringID));
                base.bw.Write((byte)0);
                base.bw.Write(StringID.Length);
            }
        }

        public void Write(Dependancy Dependancy, bool RetainCurrentPosition)
        {
            long position = base.Position;
            if (Dependancy.ClassString == "null")
            {
                base.Position += 4L;
            }
            else
            {
                base.Write(Dependancy.Class);
            }
            int index = this.Tags.IndexOf(Dependancy.ClassString, Dependancy.Path);
            if (index != -1)
            {
                base.bw.Write(this.Tags[index, H2.Streams.Sections.Tags.SearchType.Index].ID);
            }
            else
            {
                base.bw.Write(index);
            }
            if (RetainCurrentPosition)
            {
                base.Position = position;
            }
        }

        public void Write(StringID StringID, bool RetainCurrentPosition)
        {
            if (StringID.NOTINSTRINGIDTABLE)
            {
                if (!RetainCurrentPosition)
                {
                    base.bw.Write(0);
                }
            }
            else
            {
                long position = base.Position;
                base.bw.Write(this.StringIDs.IndexOf(StringID));
                base.bw.Write((byte)0);
                base.bw.Write(StringID.Length);
                if (RetainCurrentPosition)
                {
                    base.Position = position;
                }
            }
        }

        public void Write(System.Type t, object Object, int Magic, ref int EOFMainMeta)
        {
            foreach (FieldInfo info in t.GetFields(BindingFlags.Public | BindingFlags.Instance))
            {
                System.Type fieldType = info.FieldType;
                if (fieldType.IsArray)
                {
                    if (fieldType == typeof(byte[]))
                    {
                        byte[] buffer = (byte[]) info.GetValue(Object);
                        foreach (byte num in buffer)
                        {
                            base.Write(num);
                        }
                    }
                    else if (fieldType == typeof(char[]))
                    {
                        char[] chArray = (char[]) info.GetValue(Object);
                        foreach (char ch in chArray)
                        {
                            base.Write(ch);
                        }
                    }
                    else
                    {
                        Array array = (Array) info.GetValue(Object);
                        base.Write(array.Length);
                        if (array.Length > 0)
                        {
                            fieldType = fieldType.GetElementType();
                            base.Write((int) (((int) EOFMainMeta) + Magic));
                            long position = base.Position;
                            base.Position = (long) EOFMainMeta;
                            EOFMainMeta += TagStructures.GetReflexiveAttributes(array).ChunkSize * array.Length;
                            FieldInfo field = fieldType.GetField("__StartOffset__", BindingFlags.NonPublic | BindingFlags.Instance);
                            for (int i = 0; i < array.Length; i++)
                            {
                                object obj2 = array.GetValue(i);
                                field.SetValue(obj2, (int) base.Position);
                                this.Write(fieldType, obj2, Magic, ref EOFMainMeta);
                            }
                            base.Position = position;
                        }
                        else
                        {
                            base.Write(0);
                        }
                    }
                }
                else if (fieldType == typeof(byte))
                {
                    base.Write((byte) info.GetValue(Object));
                }
                else if (fieldType == typeof(float))
                {
                    base.Write((float) info.GetValue(Object));
                }
                else if (fieldType == typeof(short))
                {
                    base.Write((short) info.GetValue(Object));
                }
                else if (fieldType == typeof(int))
                {
                    base.Write((int) info.GetValue(Object));
                }
                else if (fieldType == typeof(long))
                {
                    base.Write((long) info.GetValue(Object));
                }
                else if (fieldType == typeof(ushort))
                {
                    base.Write((ushort) info.GetValue(Object));
                }
                else if (fieldType == typeof(uint))
                {
                    base.Write((uint) info.GetValue(Object));
                }
                else if (fieldType == typeof(ulong))
                {
                    base.Write((ulong) info.GetValue(Object));
                }
                else if (fieldType == typeof(H2.DataTypes.String))
                {
                    base.Write((H2.DataTypes.String) info.GetValue(Object));
                }
                else if (fieldType == typeof(Bitmask))
                {
                    base.Write((Bitmask) info.GetValue(Object));
                }
                else if (fieldType == typeof(Dependancy))
                {
                    this.Write((Dependancy) info.GetValue(Object));
                }
                else if (fieldType == typeof(StringID))
                {
                    this.Write((StringID) info.GetValue(Object));
                }
                else
                {
                    if (fieldType != typeof(H2.DataTypes.Enum))
                    {
                        throw new Exception("Unknown Type");
                    }
                    base.Write((H2.DataTypes.Enum) info.GetValue(Object));
                }
            }
        }

        public void WriteAt(int Offset, Dependancy Dependancy, bool RetainCurrentPosition)
        {
            long position = base.Position;
            base.Position = Offset;
            if (Dependancy.ClassString == "null")
            {
                base.Position += 4L;
            }
            else
            {
                base.Write(Dependancy.Class);
            }
            int index = this.Tags.IndexOf(Dependancy.ClassString, Dependancy.Path);
            if (index != -1)
            {
                base.bw.Write(this.Tags[index, H2.Streams.Sections.Tags.SearchType.Index].ID);
            }
            else
            {
                base.bw.Write(index);
            }
            if (RetainCurrentPosition)
            {
                base.Position = position;
            }
        }

        public void WriteAt(int Offset, StringID StringID, bool RetainCurrentPosition)
        {
            if (StringID.NOTINSTRINGIDTABLE)
            {
                if (!RetainCurrentPosition)
                {
                    base.bw.Write(0);
                }
            }
            else
            {
                long position = base.Position;
                base.Position = Offset;
                base.bw.Write(this.StringIDs.IndexOf(StringID));
                base.bw.Write((byte)0);
                base.bw.Write(StringID.Length);
                if (RetainCurrentPosition)
                {
                    base.Position = position;
                }
            }
        }

        public H2.Streams.Sections.Bitmaps Bitmaps
        {
            get
            {
                return this._bitmaps;
            }
            set
            {
                this._bitmaps = value;
            }
        }

        public H2.Streams.Sections.Header Header
        {
            get
            {
                return this._header;
            }
            set
            {
                this._header = value;
            }
        }

        public H2.Streams.Sections.Index Index
        {
            get
            {
                return this._index;
            }
            set
            {
                this._index = value;
            }
        }

        public H2.Streams.Sections.Sbsp Sbsp
        {
            get
            {
                return this._sbsp;
            }
            set
            {
                this._sbsp = value;
            }
        }

        public H2.Streams.Sections.StringIDs StringIDs
        {
            get
            {
                return this._stringIDs;
            }
            set
            {
                this._stringIDs = value;
            }
        }

        public H2.Streams.Sections.Tags Tags
        {
            get
            {
                return this._tags;
            }
            set
            {
                this._tags = value;
            }
        }

        public UnicodeCollection Unicode
        {
            get
            {
                return this._unicode;
            }
            set
            {
                this._unicode = value;
            }
        }
    }
}

